/*
 * Copyright (c) 2023 NVIDIA Corporation
 *
 * Licensed under the Apache License Version 2.0 with LLVM Exceptions
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *   https://llvm.org/LICENSE.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#pragma once

#include "__config.hpp"
#include "__type_traits.hpp"

#if 0 // STDEXEC_HAS_STD_RANGES()

#include <ranges>

namespace stdexec::ranges {
  using std::ranges::begin;
  using std::ranges::end;

  using std::ranges::range_value_t;
  using std::ranges::range_reference_t;
  using std::ranges::iterator_t;
  using std::ranges::sentinel_t;
}

#else

#include <iterator>

namespace stdexec::ranges
{

namespace __detail
{
void begin();
void end();

template <class _Ty>
concept __has_member_begin =
    requires(_Ty&& __v) { static_cast<_Ty&&>(__v).begin(); };

template <class _Ty>
concept __has_free_begin =
    __has_member_begin<_Ty> ||
    requires(_Ty&& __v) { begin((static_cast<_Ty&&>(__v))); };

template <class _Ty>
concept __has_member_end =
    requires(_Ty&& __v) { static_cast<_Ty&&>(__v).end(); };

template <class _Ty>
concept __has_free_end =
    __has_member_end<_Ty> ||
    requires(_Ty&& __v) { end((static_cast<_Ty&&>(__v))); };

struct __begin_t
{
    template <class _Range>
        requires __has_member_begin<_Range>
    auto operator()(_Range&& __rng) const
        noexcept(noexcept((static_cast<_Range&&>(__rng)).begin()))
            -> decltype((static_cast<_Range&&>(__rng)).begin())
    {
        return static_cast<_Range&&>(__rng).begin();
    }

    template <class _Range>
        requires __has_free_begin<_Range>
    auto operator()(_Range&& __rng) const
        noexcept(noexcept(begin((static_cast<_Range&&>(__rng)))))
            -> decltype(begin((static_cast<_Range&&>(__rng))))
    {
        return begin((static_cast<_Range&&>(__rng)));
    }
};

struct __end_t
{
    template <class _Range>
        requires __has_member_end<_Range>
    auto operator()(_Range&& __rng) const
        noexcept(noexcept((static_cast<_Range&&>(__rng)).end()))
            -> decltype((static_cast<_Range&&>(__rng)).end())
    {
        return static_cast<_Range&&>(__rng).end();
    }

    template <class _Range>
        requires __has_free_end<_Range>
    auto operator()(_Range&& __rng) const
        noexcept(noexcept(end((static_cast<_Range&&>(__rng)))))
            -> decltype(end((static_cast<_Range&&>(__rng))))
    {
        return end((static_cast<_Range&&>(__rng)));
    }
};
} // namespace __detail

inline constexpr __detail::__begin_t begin{};
inline constexpr __detail::__end_t end{};

template <class _Range>
using iterator_t = decltype(begin((__declval<_Range>())));

template <class _Range>
using sentinel_t = decltype(end((__declval<_Range>())));

template <class _Range>
using range_reference_t = decltype(*begin((__declval<_Range>())));

template <class _Range>
using range_value_t =
    typename std::iterator_traits<iterator_t<_Range>>::value_type;

} // namespace stdexec::ranges

#endif
